﻿/*-------------------------------------------------------------------------
* 命名空间: ScreenCapture.Base.Services/MyBinder
* 类       名: MyBinder
* 功      能:  TD
* 时      间:  2023/12/4 10:40:05
* 版      本:  V1.0
* 作      者:  Fioman
* 格      言:  Talk is cheap,show me the code ^_^
*-------------------------------------------------------------------------*/
using Ninject.Modules;
using System;
using System.Collections.Generic;
using System.Text;

namespace ScreenCapture.Base.Services
{
    // 服务和对应的实体类的绑定类,主要用来依赖注入的注册服务类型和具体类型的类
    // 每次MyBinder被调用的时候,都会有Ninject框架调用这个类的Load方法,然后Load方法
    // 里会自动调用模块的OnLoad方法
    // 这里需要注意的点是,要重写Name属性,以为这个是Ninject来区分模块的唯一标识,Niject框架要求相同模块名称
    // 的只能加载一次,如果重复使用了MyBinder来加载不同的模块的时候,就要给每一个模块取一个名字,名字就叫Module_Index
    // 名称然后加上Index,index是递增的,确保模块名称肯定不一样.
    /// <summary>
    /// 如果你这个MyBinder模块,这个模块实现了NinjectModule,并且你希望这个模块可以在多个地方被加载和使用.
    /// 这时,你就需要在模块中重写Name属性,返回一个独一无二的名称,这样Ninject就可以区分和管理这些模块了.
    /// </summary>
    public class MyBinder : NinjectModule, IBinder
    {
        /// <summary>
        /// 当前加载模块的索引
        /// </summary>
        private static int _currentIndex;
        /// <summary>
        /// 模块的索引
        /// </summary>
        private readonly int _index;
        /// <summary>
        /// 当前加载的模块
        /// </summary>
        private readonly IModule _module;

        /// <summary>
        /// 构造函数,参入具体的哪个模块构造的MyBinder
        /// </summary>
        /// <param name="Module"></param>
        public MyBinder(IModule Module)
        {
            _index = _currentIndex++;
            _module = Module;
        }

        // 默认绑定,带Singleton参数,默认都是单例模式绑定
        public void Bind<TFrom, TTarget>(bool Singleton = true) where TTarget : TFrom
        {
            // 而Bind<TFrom>().To<TTarge>() 绑定默认是瞬态的,如果要设置为单例的,要修改他的模式
            var binding = Bind<TFrom>().To<TTarget>();
            if (Singleton)
                binding.InSingletonScope();
        }

        // 将服务接口绑定到一个函数上,然后调用这个函数去获取一个对象实例
        public void Bind<T>(Func<T> Function, bool Singleton = true)
        {
            var binding = Bind<T>().ToMethod(M => Function());

            if (Singleton)
                binding.InSingletonScope();
        }

        /// <summary>
        /// 绑定接口和类都是单例模式,就是使用接口这是是类获取实例的时候都是获取的同一个对象.
        /// BindAsInterfaceAndClass这个方法主要作用是在依赖注入(Dependency Injection DI)的上下文中,
        /// 将某个类TClass同时注册为其自身和作为某个接口的TInterface的实现
        /// </summary>
        /// <typeparam name="TInterface"></typeparam>
        /// <typeparam name="TClass"></typeparam>
        /// <exception cref="NotImplementedException"></exception>
        public void BindAsInterfaceAndClass<TInterface, TClass>() where TClass : TInterface
        {
            // TClass 注册为单例(Singleton),也就是说整个应用程序生命周期内,这个类只能被实例化一次
            // 一会每次获取这个类的实例的时候,都返回同一个实例
            BindSingleton<TClass>();
            // 这一行的作用是将TInterface接口和TClass进行绑定.在后续代码中,如果需要TInterface的实例,
            // 那么DI容器会返回一个TClass实例.因为TClass是单例,所以每次获取TInterface的实例时,实际上
            // 返回的都是同一个TClass的实例
            // 总之,这两行代码就是告诉DI容器:无论何时我请求一个TClass或者是TInterface,我都希望获取的是
            // 相同的一个TClass实例
            Bind<TInterface>(() => Get<TClass>());
        }

        // 单例模式绑定
        public void BindSingleton<T>()
        {
            Bind<T>().ToSelf().InSingletonScope();
        }

        public T Get<T>()
        {
            throw new NotImplementedException();
        }

        // 模块加载,这个是Ninject自动调用的函数,然后这个函数又调用了每个模块的
        // 自定义的模块加载函数OnLoad()函数,每个模块去实现自己的服务和具体类
        // 的绑定定义
        public override void Load()
        {
            _module?.OnLoad(this);
        }

        public override string Name => $"Module{_index}";
    }
}
